#ifndef _LIB_KERNEL_THREAD_H_
#define _LIB_KERNEL_THREAD_H_

#include "interrupt.h"
#include "list.h"
#include "memory.h"
#include "stdint.h"

typedef int16_t pid_t;

extern struct list thread_ready_list;
extern struct list thread_all_list;

typedef void thread_func(void*);

enum task_status
{
  TASK_RUNNING,
  TASK_READY,
  TASK_BLOCKED,
  TASK_WAITING,
  TASK_HANGING,
  TASK_DIED
};

// 中断栈
struct intr_stack
{
  // 保存当前进程部分上下文信息,
  // 以下内容是kernel.S中实现的中断处理程序intr%1entry宏中压入的
  // 还有一部分进程上下文信息, 这部分内容在switch.S中的switch中压入
  uint32_t vec_no;
  uint32_t edi;
  uint32_t esi;
  uint32_t ebp;
  uint32_t esp_dummy;
  uint32_t ebx;
  uint32_t edx;
  uint32_t ecx;
  uint32_t eax;
  uint32_t gs;
  uint32_t fs;
  uint32_t es;
  uint32_t ds;

  // 下面这些内容是中断发生的时候CPU自动填充的，并且在中断返回时自动弹出
  // 如果目标代码段的访问权限高于当前代码段的权限，则需要保存当前SS、ESP
  // 如果目标代码段的访问权限不高于当前代码段的权限，则直接使用当前的SS、ESP即可，此时不会压入SS和ESP
  // 所以此时最后两个esp和ss的值无关的
  uint32_t err_code;
  void (*eip)(void);
  uint32_t cs;
  uint32_t eflags;
  void*    esp;
  uint32_t ss;
};
// 线程栈
struct thread_stack
{
  // 保存当前进程部分上下文信息->ebp,
  // 这部分内容在switch.S中的switch中压入，还有一部分信息在intr_stack中保存，在kernel.S中由intr%1entry压入
  uint32_t ebp;
  // 保存当前进程部分上下文信息->ebx,
  // 这部分内容在switch.S中的switch中压入，还有一部分信息在intr_stack中保存，在kernel.S中由intr%1entry压入
  uint32_t ebx;
  // 保存当前进程部分上下文信息->edi,
  // 这部分内容在switch.S中的switch中压入，还有一部分信息在intr_stack中保存，在kernel.S中由intr%1entry压入
  uint32_t edi;
  // 保存当前进程部分上下文信息->esi,
  // 这部分内容在switch.S中的switch中压入，还有一部分信息在intr_stack中保存，在kernel.S中由intr%1entry压入
  uint32_t esi;

  // 线程初次上CPU前eip指向start_thread_with_func,即指向线程运行函数。其他时候，CPU指向swith_to的返回地址
  void (*eip)(thread_func* func, void* func_args);

  // 第一次上CPU，调用的是start_thread_with_func这个C语言程序，所以此时栈顶就是返回地址
  void(*ununsed_retaddr); //< 该参数只在第一次调度上CPU时候被使用
  thread_func* thead_func;
  void*        func_arg;
};

/*
 * @brief Process Control Block (PCB)
 * @details Basic information of kernel threads
 */
struct task_struct
{
  /// @brief Stack pointer of the kernel thread's TCB,
  /// should be set to point to the end of a page during initialization
  uint32_t* self_kstack;

  /// @brief Process id
  pid_t pid;

  /// @brief Status of the kernel thread's TCB
  enum task_status status;
  /// @brief Name of the kernel thread's TCB
  char name[16];
  /// @brief Priority of the kernel thread's TCB
  uint8_t priority;
  /// @brief Number of ticks the kernel thread's TCB can run on the CPU each
  /// time
  uint8_t ticks_can_run;
  /// @brief Total number of ticks the kernel thread's TCB has been running
  /// since creation
  uint32_t thread_total_ticks;

  /// @brief Tag for the list containing ready threads
  struct list_elem ready_list_tag;
  /// @brief Tag for the list containing all threads
  struct list_elem all_list_tag;

  /// @brief virtual address of process PDE(page directory entry)
  uint32_t* pg_dir_entry;
  /// @brief virtual_address structure for user process
  struct virtual_addr usrprog_vaddr;

  struct mem_block_desc u_block_desc[DESC_CNT];

  /// @brief magic number for stack verification
  uint32_t stack_magic;
};

/**
 * @brief Encapsulates a function as a thread execution stream.
 *
 * @details This function serves as a wrapper to encapsulate a provided thread
 * function and its argument as a thread execution stream. It takes the
 * specified 'function' and 'func_arg' as inputs and invokes 'function' with
 * 'func_arg' as its argument.
 *
 * @param function A pointer to the thread function to be executed.
 * @param func_arg The argument to be passed to the thread function.
 */
static void start_thread_with_func(thread_func* function, void* func_arg)
{
  // enable intrrupt when thread execute at the first time
  // or the system would not work properly
  intr_enable();
  // Invoke the provided thread function with the given argument.
  function(func_arg);
}

/**
 * @brief Initializes a thread represented by the provided task structure.
 *
 * This function initializes a thread by setting its attributes such as name,
 * status, priority, stack, and a stack magic number for debugging purposes.
 *
 * @param pthread   Pointer to the task structure representing the thread.
 * @param name      The name of the thread.
 * @param priority  The priority of the thread.
 */
void init_thread_PCB_info(struct task_struct* pthread, char* name,
                          int priority);

/**
 * @brief Initializes a new thread within the provided task structure.
 *
 * This function prepares a new thread within the specified 'pthread' task
 * structure. It allocates space for the interrupt stack and the thread stack,
 * initializes the thread stack with relevant information, and sets certain
 * registers to initial values.
 *
 * @param pthread      Pointer to the task structure representing the thread.
 * @param function     The entry point function of the thread.
 * @param func_arg     The argument to be passed to the entry point function.
 */
void init_thread_stack_func_info(struct task_struct* pthread,
                                 thread_func function, void* func_arg);

/**
 * @brief Starts a new thread with the provided attributes and entry point.
 *
 * This function initiates the creation of a new thread with the given 'name',
 * 'prio' (priority), 'function' (entry point), and 'func_arg' (function
 * argument). It allocates memory for the thread, initializes it, and sets up
 * its stack. Finally, it transfers control to the new thread by loading its
 * stack pointer and other registers.
 *
 * @param name      The name of the new thread.
 * @param prio      The priority of the new thread.
 * @param function  The entry point function for the new thread.
 * @param func_arg  The argument to be passed to the entry point function.
 *
 * @return A pointer to the task structure representing the new thread
 */
struct task_struct* thread_start(char* name, int priority, thread_func function,
                                 void* func_arg);

struct task_struct* cur_thread_PCB_get();

/**
 * @brief Perform thread scheduling
 *
 * @details
 *  This function is responsible for switching the current thread out of the
 * processor and selecting the next runnable thread from the ready queue to be
 * scheduled onto the processor.
 *
 *  The scheduler is responsible for making decisions about which thread should
 * execute next, based on factors such as thread priority, time slices, or other
 * scheduling policies.
 */
void schedule();

/**
 * @brief Initializes the thread system
 */
void thread_init();

/**
 * @brief Blocks the current thread and sets its status to the provided
 * task_status
 *
 * @param task_status The status to set the thread to after blocking
 */
void thread_block(enum task_status state);

/**
 * @brief Unblocks a specific thread
 *
 * @param pthread Pointer to the task_struct of the thread to be unblocked
 */
void thread_unblock(struct task_struct* pthread);

#endif
